package com.sevensoft.framework.context.support;

import com.sevensoft.framework.beans.BeanDefinition;
import com.sevensoft.framework.beans.MutablePropertyValues;
import com.sevensoft.framework.beans.PropertyValue;
import com.sevensoft.framework.beans.factory.support.BeanDefinitionRegistry;
import com.sevensoft.framework.beans.factory.xml.XmlBeanDefinitionReader;
import com.sevensoft.framework.utils.StringUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;

/**
 * IOC容器具体子类
 * （用于加载类路径下的xml文件）
 */
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {


    public ClassPathXmlApplicationContext(String configLocation) {
        this.configLocation = configLocation;
        beanDefinitionReader = new XmlBeanDefinitionReader();
        try {
            this.refresh();
        }
        catch (Exception e) {
//            throw new RuntimeException("Error");
        }
    }

    /**
     * 通过name获取Bean
     *
     * @param name
     * @return
     * @throws Exception
     */
    @Override
    public Object getBean(String name) throws Exception {
        //判断容器中是否存在该名称的bean，有则返回，否则自行创建
        Object obj = singletonObjects.get(name);
        if ( Objects.nonNull(obj) )
            return obj;
        //获取beanDefinition
        BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
        BeanDefinition beanDefinition = registry.getBeanDefinition(name);
        //获取bean信息中的className
        String className = beanDefinition.getClassName();
        //通过反射创建对象
        Class<?> clazz = Class.forName(className);
        Object newObj = clazz.newInstance();
        //进行依赖注入
        MutablePropertyValues propertyValues = beanDefinition.getMutablePropertyValues();
        for ( PropertyValue propertyValue : propertyValues ) {
            //获取name
            String propertyName = propertyValue.getName();
            //获取value
            String value = propertyValue.getValue();
            //获取ref
            String ref = propertyValue.getRef();
            if ( Objects.nonNull(ref) && !ref.isEmpty() ) {
                //获取依赖的bean对象
                Object bean = getBean(ref);
                //获取方法对象
                Method[] methods = clazz.getMethods();
                Arrays.stream(methods).forEach(method -> {
                    try {
                        //执行setter方法注入
                        method.invoke(newObj, bean);
                    }
                    catch (Exception ignored) {
                    }
                });
            }
            if ( Objects.nonNull(value) && !value.isEmpty() ) {
                //拼接方法名
                String methodByFieldName = StringUtils.getSetterMethodByFieldName(propertyName);
                //获取method对象
                Method method = clazz.getMethod(methodByFieldName, String.class);
                method.invoke(newObj, value);
            }
        }
        //存储到容器中
        singletonObjects.put(name, newObj);
        return newObj;
    }

    /**
     * 通过name和字节码文件获取Bean
     *
     * @param name
     * @param clazz
     * @return
     * @throws Exception
     */
    @Override
    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
        Object bean = getBean(name);
        if ( Objects.isNull(bean) )
            return null;
        return clazz.cast(bean);
    }
}
